home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac Mania 5
/
MacMania 5.toast
/
/
Internet software
/
NewsWatcher
/
NW Source
/
Source
/
subscribe.c
< prev
next >
Wrap
Text File
|
1997-01-09
|
20KB
|
751 lines
/*----------------------------------------------------------------------------
subscribe.c
This module handles subscribing and unsubscribing to groups.
Copyright © 1994-1997, Northwestern University.
----------------------------------------------------------------------------*/
#include <string.h>
#include <stdio.h>
#include "glob.h"
#include "dialog.h"
#include "child.h"
#include "newswatcher.h"
#include "subscribe.h"
#include "news.h"
#include "memutil.h"
#include "windutil.h"
#include "strutil.h"
#include "group.h"
#include "full.h"
#include "biglist.h"
#define kDuplicateGroupsAlert 151
/*----------------------------------------------------------------------------
PresentDuplicateGroupsAlert
Present the duplicate groups alert.
Entry: numDuplicates = number of duplicate groups.
dupNameOffset = offset in gGroupNames of duplicate group
name if numDuplicates = 1.
drag = true if drag and drop, false if paste.
Exit: function result = error code.
----------------------------------------------------------------------------*/
static OSErr PresentDuplicateGroupsAlert (long numDuplicates,
long dupNameOffset, Boolean drag)
{
CStr255 msg, fmt, groupName, movingOrPasting;
DialogPtr dlg;
short len, groupNameLen, item;
OSErr err = noErr;
GetCString(drag ? kStrMoving : kStrPasting, movingOrPasting);
if (numDuplicates > 1) {
GetCString(kStrDuplicateGroups, fmt);
sprintf(msg, fmt, movingOrPasting, movingOrPasting);
} else {
GetCString(kStrOneDuplicateGroup, fmt);
strcpy(groupName, *gGroupNames + dupNameOffset);
groupNameLen = strlen(groupName);
len = strlen(fmt) + groupNameLen + strlen(movingOrPasting) - 4;
if (len > 255) {
groupNameLen -= len - 255;
groupName[groupNameLen-3] = '.';
groupName[groupNameLen-2] = '.';
groupName[groupNameLen-1] = '.';
groupName[groupNameLen] = 0;
}
sprintf(msg, fmt, groupName, movingOrPasting);
}
c2pstr(msg);
err = MyGetNewDialog(kDuplicateGroupsAlert, ok, cancel, &dlg);
if (err != noErr) return err;
ParamText((StringPtr)msg, "\p", "\p", "\p");
SysBeep(0);
MyModalDialog(dlg, gDialogFilterUPP, &item);
err = DoClose(dlg);
if (err != noErr) return err;
if (item == cancel) return userCanceledErr;
return noErr;
}
/*----------------------------------------------------------------------------
ClearStatus
Clear the status fields in all groups in a user group window.
Entry: wind = pointer to user group list window.
----------------------------------------------------------------------------*/
static void ClearStatus (WindowPtr wind)
{
TWindow **info;
BigListRef groupList;
TGroup **groupArray;
long item, index, numItems;
info = (TWindow**)GetWRefCon(wind);
groupList = (**info).groupList;
groupArray = (**info).groupArray;
numItems = BigLGetNumItems(groupList);
for (item = 0; item < numItems; item++) {
index = BigLGetData(groupList, item);
(*groupArray)[index].status = ' ';
}
}
/*----------------------------------------------------------------------------
GroupInList
Check to see if a named group appears in a user group list window, and
set the status field to 'x' if the group does appear.
Entry: nameOffset = offset in gGroupNames of group name.
wind = pointer to user group list window.
Exit: function result = true if group in window.
----------------------------------------------------------------------------*/
static Boolean GroupInList (long nameOffset, WindowPtr wind)
{
TWindow **info;
BigListRef groupList;
TGroup **groupArray;
long numItems, index, item;
info = (TWindow**)GetWRefCon(wind);
groupList = (**info).groupList;
groupArray = (**info).groupArray;
numItems = BigLGetNumItems(groupList);
for (item = 0; item < numItems; item++) {
index = BigLGetData(groupList, item);
if ((*groupArray)[index].nameOffset == nameOffset) {
(*groupArray)[index].status = 'x';
return true;
}
}
return false;
}
/*----------------------------------------------------------------------------
RemoveDuplicates
Remove duplicate groups (the ones marked with status 'x').
Entry: wind = pointer to user group list window.
Exit: function result = error code.
----------------------------------------------------------------------------*/
static OSErr RemoveDuplicates (WindowPtr wind)
{
WindowPtr child;
TWindow **info;
BigListRef groupList;
TGroup **groupArray, groupInfo;
long numDel = 0;
OSErr err = noErr;
long index, numItems, item;
info = (TWindow**)GetWRefCon(wind);
groupList = (**info).groupList;
groupArray = (**info).groupArray;
numItems = BigLGetNumItems(groupList);
for (item = 0; item < numItems; item++) {
index = BigLGetData(groupList, item);
groupInfo = (*groupArray)[index];
if (groupInfo.status == 'x') {
if ((child = FindChild(wind, item)) != nil) {
err = DoClose(child);
if (err != noErr) return err;
}
DisposeGroupUnreadList(&groupInfo);
(*groupArray)[index] = groupInfo;
BigLDeleteItems(groupList, item, 1);
numDel++;
item--;
numItems--;
}
}
return noErr;
}
/*----------------------------------------------------------------------------
RedrawGroupCount
Redraw the count in the panel area of a group window.
Entry: wind = pointer to group window.
----------------------------------------------------------------------------*/
static void RedrawGroupCount (WindowPtr wind)
{
GrafPtr port;
TWindow **info;
Rect r;
GetPort(&port);
SetPort(wind);
info = (TWindow**)GetWRefCon(wind);
r = wind->portRect;
r.bottom = (**info).panelHeight-3;
InvalRect(&r);
SetPort(port);
}
/*----------------------------------------------------------------------------
RemoveGroupFromUnsubscribedList
Remove a group from the unsubscribed list.
Entry: groupName = group name.
unsubscribed = handle to unsubscribed list.
----------------------------------------------------------------------------*/
void RemoveGroupFromUnsubscribedList (char *groupName, Handle unsubscribed)
{
long len, unsubscribedLen;
char *p, *pEnd, *q;
if (unsubscribed == nil) return;
len = strlen(groupName);
unsubscribedLen = MyGetHandleSize(unsubscribed);
p = *unsubscribed;
pEnd = p + unsubscribedLen;
while (p < pEnd) {
q = p;
while (q < pEnd && *q != CR) q++;
q++;
if (MyStrNEqual(p, groupName, len)) break;
p = q;
}
if (p > pEnd) return;
len = q-p;
BlockMoveData(q, p, pEnd-q);
MySetHandleSize(unsubscribed, unsubscribedLen - len);
}
/*----------------------------------------------------------------------------
AddNewGroup
Add a new group to a user group window.
Entry: nameOffset = offset in gGroupNames of group name to add.
wind = pointer to user group window.
pos = position of new user group list entry (item number
of new item). Pass kMaxLong to add at end of list.
theGroup = pointer to group info to add, or nil to get info
for new group from server.
cloneUnreadList = true to clone a copy of the unread list
in theGroup.
Exit: function result = error code.
----------------------------------------------------------------------------*/
OSErr AddNewGroup (long nameOffset, WindowPtr wind, long pos,
TGroup *theGroup, Boolean cloneUnreadList)
{
BigListRef groupList;
TWindow **info;
TGroup **groupArray, newGroup;
long newGroupIndex, numItems;
OSErr err = noErr;
Boolean groupExists;
TUnread **unread, **newUnread, **prevNewUnread;
CStr255 groupName;
newGroup.firstMess = 1;
newGroup.lastMess = 0;
newGroup.numUnread = 0;
newGroup.unread = nil;
newGroup.status = ' ';
info = (TWindow**)GetWRefCon(wind);
groupList = (**info).groupList;
groupArray = (**info).groupArray;
strcpy(groupName, *gGroupNames + nameOffset);
if (theGroup == nil) {
newGroup.nameOffset = nameOffset;
err = GetGroupArticleRange(&newGroup, &groupExists);
if (err != noErr) goto exit;
if (groupExists && newGroup.firstMess <= newGroup.lastMess) {
err = MyNewHandle(sizeof(TUnread), &unread);
if (err != noErr) goto exit;
(**unread).firstUnread = newGroup.firstMess;
(**unread).lastUnread = newGroup.lastMess;
(**unread).next = nil;
newGroup.unread = unread;
}
} else {
newGroup = *theGroup;
if (cloneUnreadList) {
newGroup.unread = nil;
unread = theGroup->unread;
prevNewUnread = nil;
while (unread != nil) {
err = MyNewHandle(sizeof(TUnread), &newUnread);
if (err != noErr) goto exit;
**newUnread = **unread;
(**newUnread).next = nil;
if (prevNewUnread == nil) {
newGroup.unread = newUnread;
} else {
(**prevNewUnread).next = newUnread;
}
prevNewUnread = newUnread;
unread = (**unread).next;
}
}
}
err = MySetHandleSize(groupArray, MyGetHandleSize(groupArray) + sizeof(TGroup));
if (err != noErr) goto exit;
newGroupIndex = (**info).numGroups;
(**info).numGroups++;
(*groupArray)[newGroupIndex] = newGroup;
numItems = BigLGetNumItems(groupList);
if (pos > numItems) pos = numItems;
BigLAddItems(groupList, pos, 1);
BigLSetData(groupList, pos, newGroupIndex);
RemoveGroupFromUnsubscribedList(groupName, (**info).unsubscribed);
(**info).changed = true;
return noErr;
exit:
DisposeGroupUnreadList(&newGroup);
return err;
}
/*----------------------------------------------------------------------------
CopyGroupsFromScrap
Copy groups from scrap data to a user group list window.
Entry: data = handle to scrap data. WARNING: The scrap data is
modified by this function.
wind = pointer to destination user group list window.
pos = position of new user group list items in destination
window (starting item number of new items).
Pass kMaxLong to add at end of list.
Exit: function result = error code.
----------------------------------------------------------------------------*/
OSErr CopyGroupsFromScrap (Handle data, WindowPtr wind, long pos)
{
TWindow **info;
BigListRef groupList;
long dataPos, groupNameLen, nameOffset, numUnreadPairs;
long numGroups, i, dupNameOffset;
long firstPos;
CStr255 groupName;
TGroup theGroup;
TUnread **prevUnread, **unread;
OSErr err = noErr;
long numSubscribed, item, numItems;
long numDuplicates, numNotInFullGroupList;
long j, jDataPos, jGroupNameLen;
theGroup.unread = nil;
err = CheckScrapData(data);
if (err != noErr) goto exit;
info = (TWindow**)GetWRefCon(wind);
groupList = (**info).groupList;
numItems = BigLGetNumItems(groupList);
if (pos > numItems) pos = numItems;
dataPos = 12;
numGroups = *(long*)(*data + dataPos);
dataPos += sizeof(long);
ClearStatus(wind);
/* Check for duplicate groups, both duplicates within the scrap data
and duplicates between the scrap data and the target window. Also
check for groups which no longer exist in the full group list.
For each group in the scrap data, the byte immediately following
the group name length byte is set to 0 to skip the group, either
because it is a duplicate within the scrap data or because it no
longer exists in the full group list. */
numDuplicates = 0;
numNotInFullGroupList = 0;
for (i = 0; i < numGroups; i++) {
groupNameLen = *(unsigned char*)(*data + dataPos);
dataPos++;
BlockMoveData(*data + dataPos, groupName, groupNameLen);
*(groupName + groupNameLen) = 0;
nameOffset = FindGroupOffset(groupName);
if (nameOffset == -1) {
numNotInFullGroupList++;
} else {
for (j = 0, jDataPos = 16; j < i; j++) {
jGroupNameLen = *(unsigned char*)(*data + jDataPos);
jDataPos++;
if (jGroupNameLen == groupNameLen &&
strncmp(*data + jDataPos, groupName, groupNameLen) == 0)
{
nameOffset = -1;
break;
}
jDataPos += jGroupNameLen;
jDataPos = ((jDataPos + 3) >> 2) << 2;
jDataPos += 3*sizeof(long);
numUnreadPairs = *(long*)(*data + jDataPos);
jDataPos += sizeof(long);
jDataPos += numUnreadPairs * 2 * sizeof(long);
}
if (nameOffset >= 0 && GroupInList(nameOffset, wind)) {
dupNameOffset = nameOffset;
numDuplicates++;
}
}
if (nameOffset == -1) *(*data + dataPos) = 0;
dataPos += groupNameLen;
dataPos = ((dataPos + 3) >> 2) << 2;
dataPos += 3*sizeof(long);
numUnreadPairs = *(long*)(*data + dataPos);
dataPos += sizeof(long);
dataPos += numUnreadPairs * 2 * sizeof(long);
}
if (numDuplicates > 0) {
err = PresentDuplicateGroupsAlert(numDuplicates, dupNameOffset, true);
if (err != noErr) goto exit;
}
dataPos = 16;
firstPos = pos;
numSubscribed = 0;
for (i = 0; i < numGroups; i++) {
groupNameLen = *(unsigned char*)(*data + dataPos);
dataPos++;
BlockMoveData(*data + dataPos, groupName, groupNameLen);
dataPos += groupNameLen;
dataPos = ((dataPos + 3) >> 2) << 2;
if (*groupName == 0) {
dataPos += 3*sizeof(long);
numUnreadPairs = *(long*)(*data + dataPos);
dataPos += sizeof(long);
dataPos += numUnreadPairs * 2 * sizeof(long);
} else {
*(groupName + groupNameLen) = 0;
nameOffset = FindGroupOffset(groupName);
theGroup.nameOffset = nameOffset;
theGroup.firstMess = *(long*)(*data + dataPos);
dataPos += sizeof(long);
theGroup.lastMess = *(long*)(*data + dataPos);
dataPos += sizeof(long);
theGroup.numUnread = *(long*)(*data + dataPos);
dataPos += sizeof(long);
numUnreadPairs = *(long*)(*data + dataPos);
dataPos += sizeof(long);
prevUnread = nil;
while (numUnreadPairs--) {
err = MyNewHandle(sizeof(TUnread), &unread);
if (err != noErr) goto exit;
(**unread).firstUnread = *(long*)(*data + dataPos);
dataPos += sizeof(long);
(**unread).lastUnread = *(long*)(*data + dataPos);
dataPos += sizeof(long);
(**unread).next = nil;
if (prevUnread == nil) {
theGroup.unread = unread;
} else {
(**prevUnread).next = unread;
}
prevUnread = unread;
}
if (theGroup.firstMess >= 0) {
err = AddNewGroup(nameOffset, wind, pos, &theGroup, false);
if (err != noErr) goto exit;
theGroup.unread = nil;
} else {
err = AddNewGroup(nameOffset, wind, pos, nil, false);
if (err != noErr) goto exit;
theGroup.unread = nil;
}
pos++;
numSubscribed++;
}
}
if (numNotInFullGroupList) NoteMessageNumber(kStrSomeGroupsNotInFullGroupList);
if (numSubscribed == 0) goto exit;
BigLSelectAll(groupList, false);
for (item = firstPos; item < firstPos + numSubscribed; item++)
BigLSelect(groupList, item, true);
if (numDuplicates > 0) {
err = RemoveDuplicates(wind);
if (err != noErr) goto exit;
}
if (gPrefs.reZoomWindows) {
err = DoZoom(wind, inZoomOut);
if (err != noErr) goto exit;
} else {
SetWindowNeedsZooming(wind);
}
RedrawGroupCount(wind);
return noErr;
exit:
DisposeGroupUnreadList(&theGroup);
return err;
}
/*----------------------------------------------------------------------------
CopyOrMoveSelectedGroups
Copy or move selected groups from a group list window to a user group
list window.
Entry: srcWindow = pointer to source group list window.
destWindow = pointer to destination user group list window.
pos = position of new user group list items in destination
window (starting item number of new items).
Pass kMaxLong to add at end of list.
copy = true to copy groups, false to move groups.
Exit: function result = error code.
----------------------------------------------------------------------------*/
OSErr CopyOrMoveSelectedGroups (WindowPtr srcWindow, WindowPtr destWindow, long pos,
Boolean copy)
{
long srcItem, destItem;
TWindow **srcInfo, **destInfo;
TGroup **srcGroupArray, srcGroup;
long **srcGroupNameOffsets;
BigListRef srcList, destList;
long index;
long numSelected=0, firstPos, numGroups;
OSErr err = noErr;
TGroupWindowKind srcKind;
long numDuplicates;
long nameOffset, dupNameOffset;
srcInfo = (TWindow**)GetWRefCon(srcWindow);
srcList = (**srcInfo).groupList;
srcGroupArray = (**srcInfo).groupArray;
srcGroupNameOffsets = (**srcInfo).groupNameOffsets;
srcKind = (**srcInfo).groupKind;
destInfo = (TWindow**)GetWRefCon(destWindow);
destList = (**destInfo).groupList;
numGroups = BigLGetNumItems(destList);
if (pos > numGroups) pos = numGroups;
ClearStatus(destWindow);
srcItem = BigLGetFirstSelectedItem(srcList);
numDuplicates = 0;
while (srcItem >= 0) {
index = BigLGetData(srcList, srcItem);
if (srcKind == kUserGroup) {
nameOffset = (*srcGroupArray)[index].nameOffset;
} else {
nameOffset = (*srcGroupNameOffsets)[index];
}
if (GroupInList(nameOffset, destWindow)) {
dupNameOffset = nameOffset;
numDuplicates++;
}
srcItem = BigLGetNextSelectedItem(srcList, srcItem+1);
}
if (numDuplicates > 0) {
err = PresentDuplicateGroupsAlert(numDuplicates, dupNameOffset, true);
if (err != noErr) return err;
}
srcItem = BigLGetFirstSelectedItem(srcList);
firstPos = pos;
while (srcItem >= 0) {
numSelected++;
index = BigLGetData(srcList, srcItem);
if (srcKind == kUserGroup) {
srcGroup = (*srcGroupArray)[index];
err = AddNewGroup(srcGroup.nameOffset, destWindow, pos,
&srcGroup, copy);
if (err != noErr) return err;
if (!copy) {
srcGroup.unread = nil;
(*srcGroupArray)[index] = srcGroup;
}
} else {
err = AddNewGroup((*srcGroupNameOffsets)[index], destWindow, pos,
nil, false);
if (err != noErr) return err;
}
pos++;
srcItem = BigLGetNextSelectedItem(srcList, srcItem+1);
}
if (numSelected == 0) return noErr;
if (!copy) {
err = UnsubscribeSelected(srcWindow);
if (err != noErr) return err;
}
BigLSelectAll(destList, false);
for (destItem = firstPos; destItem < firstPos + numSelected; destItem++)
BigLSelect(destList, destItem, true);
if (numDuplicates > 0) {
err = RemoveDuplicates(destWindow);
if (err != noErr) return err;
}
if (gPrefs.reZoomWindows) {
err = DoZoom(destWindow, inZoomOut);
if (err != noErr) return err;
} else {
SetWindowNeedsZooming(destWindow);
}
RedrawGroupCount(destWindow);
return noErr;
}
/*----------------------------------------------------------------------------
AddGroupToUnsubscribedList
Add a group to the unsubscribed list.
Entry: groupName = group name.
unsubscribed = handle to unsubscribed list.
Exit: function result = error code.
----------------------------------------------------------------------------*/
OSErr AddGroupToUnsubscribedList (char *groupName, Handle unsubscribed)
{
long len, unsubscribedLen;
char *p, *pEnd, *q;
OSErr err = noErr;
if (unsubscribed == nil) return noErr;
len = strlen(groupName);
unsubscribedLen = MyGetHandleSize(unsubscribed);
p = *unsubscribed;
pEnd = p + unsubscribedLen;
while (p < pEnd) {
q = p;
while (q < pEnd && *q != CR) q++;
q++;
if (MyStrNEqual(p, groupName, len)) break;
p = q;
}
if (p < pEnd) return noErr;
err = MySetHandleSize(unsubscribed, unsubscribedLen + len + 2);
if (err != noErr) return err;
p = *unsubscribed + unsubscribedLen;
BlockMoveData(groupName, p, len);
p += len;
*p++ = '!';
*p++ = CR;
return noErr;
}
/*----------------------------------------------------------------------------
UnsubscribeSelected
Unsubscribe selected newsgroups.
Entry: wind = pointer to user group list window.
Exit: function result = error code.
----------------------------------------------------------------------------*/
OSErr UnsubscribeSelected (WindowPtr wind)
{
WindowPtr child;
TWindow **info;
BigListRef groupList;
TGroup **groupArray, groupInfo;
long item, index, numDel = 0;
OSErr err = noErr;
CStr255 groupName;
info = (TWindow**)GetWRefCon(wind);
groupList = (**info).groupList;
groupArray = (**info).groupArray;
item = BigLGetFirstSelectedItem(groupList);
while (item >= 0) {
index = BigLGetData(groupList, item);
if ((child = FindChild(wind, item)) != nil) {
err = DoClose(child);
if (err != noErr) return err;
}
groupInfo = (*groupArray)[index];
DisposeGroupUnreadList(&groupInfo);
(*groupArray)[index] = groupInfo;
strcpy(groupName, *gGroupNames + groupInfo.nameOffset);
err = AddGroupToUnsubscribedList(groupName, (**info).unsubscribed);
if (err != noErr) return err;
BigLDeleteItems(groupList, item, 1);
numDel++;
item = BigLGetNextSelectedItem(groupList, item);
}
if (numDel > 0) {
(**info).changed = true;
RedrawGroupCount(wind);
SetWindowNeedsZooming(wind);
}
return noErr;
}